package top.dyzmj.detty.core.utils.internal;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.dyzmj.detty.core.utils.SystemPropertyUtil;
import top.dyzmj.detty.core.utils.concurrent.FastThreadLocalThread;

import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 描述:
 * 为 Detty和所有FastThreadLocals存储线程局部变量的内部数据结构。
 * 请注意，这个类仅供内部使用，随时可能更改。
 * 使用FastThreadLocal，除非你知道你在做什么。
 *
 * @author dongYu
 * @date 2021/11/24
 */
public final class InternalThreadLocalMap {

	private static final Logger logger = LoggerFactory.getLogger(InternalThreadLocalMap.class);

	private static final ThreadLocal<InternalThreadLocalMap> SLOW_THREAD_LOCAL_MAP = new ThreadLocal<>();

	private static final AtomicInteger NEXT_INDEX = new AtomicInteger();
	private static final int STRING_BUILDER_INITIAL_SIZE;
	private static final int STRING_BUILDER_MAX_SIZE;
	private static final int INDEXED_VARIABLE_TABLE_INITIAL_SIZE = 32;

	public static final Object UNSET = new Object();
	private int futureListenerStackDepth;

	static {
		STRING_BUILDER_INITIAL_SIZE =
				SystemPropertyUtil.getInt("top.detty.threadLocalMap.stringBuilder.initialSize", 1024);
		logger.debug("-Dtop.detty.threadLocalMap.stringBuilder.initialSize: {}", STRING_BUILDER_INITIAL_SIZE);

		STRING_BUILDER_MAX_SIZE = SystemPropertyUtil.getInt("io.netty.threadLocalMap.stringBuilder.maxSize", 1024 * 4);
		logger.debug("-Dtop.detty.threadLocalMap.stringBuilder.maxSize: {}", STRING_BUILDER_MAX_SIZE);
	}

	/**
	 * FastThreadLocal 使用
	 */
	private Object[] indexedVariables;
	// Core thread-locals
	private ThreadLocalRandom random;

	// 字符串相关线程本地
	private StringBuilder stringBuilder;

	private InternalThreadLocalMap() {
		indexedVariables = newIndexedVariableTable();
	}

	/**
	 * 获取 InternalThreadLocalMap
	 */
	public static InternalThreadLocalMap getIfSet() {
		Thread thread = Thread.currentThread();
		if (thread instanceof FastThreadLocalThread) {
			return ((FastThreadLocalThread) thread).threadLocalMap();
		}

		return SLOW_THREAD_LOCAL_MAP.get();
	}

	/**
	 * 获取 InternalThreadLocalMap
	 */
	public static InternalThreadLocalMap get() {
		Thread thread = Thread.currentThread();
		if (thread instanceof FastThreadLocalThread) {
			return fastGet((FastThreadLocalThread) thread);
		}
		return slowGet();
	}

	/**
	 * 移除 InternalThreadLocalMap
	 */
	public static void remove() {
		Thread thread = Thread.currentThread();
		if (thread instanceof FastThreadLocalThread) {
			((FastThreadLocalThread) thread).setThreadLocalMap(null);
		}
		SLOW_THREAD_LOCAL_MAP.remove();
	}

	/**
	 * 移除 InternalThreadLocalMap
	 */
	public static void destroy() {
		SLOW_THREAD_LOCAL_MAP.remove();
	}

	/**
	 * 获取下一个变量的索引
	 */
	public static int nextVariableIndex() {
		int index = NEXT_INDEX.getAndIncrement();
		if (index < 0) {
			NEXT_INDEX.decrementAndGet();
			throw new IllegalStateException("too many thread-local indexed variables");
		}
		return index;
	}

	/**
	 * 上一个变量的索引
	 */
	public static int lastVariableIndex() {
		return NEXT_INDEX.get() - 1;
	}

	private static Object[] newIndexedVariableTable() {
		Object[] array = new Object[INDEXED_VARIABLE_TABLE_INITIAL_SIZE];
		Arrays.fill(array, UNSET);
		return array;
	}

	private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {
		InternalThreadLocalMap threadLocalMap = thread.threadLocalMap();
		if (threadLocalMap == null) {
			threadLocalMap = new InternalThreadLocalMap();
			thread.setThreadLocalMap(threadLocalMap);
		}
		return threadLocalMap;
	}

	private static InternalThreadLocalMap slowGet() {
		InternalThreadLocalMap threadLocalMap = SLOW_THREAD_LOCAL_MAP.get();
		if (threadLocalMap == null) {
			threadLocalMap = new InternalThreadLocalMap();
			SLOW_THREAD_LOCAL_MAP.set(threadLocalMap);
		}
		return threadLocalMap;
	}

	public Object indexedVariable(int index) {
		Object[] lookup = indexedVariables;
		return index < lookup.length ? lookup[index] : UNSET;
	}

	public boolean setIndexedVariable(int index, Object value) {
		Object[] lookup = indexedVariables;
		if (index < lookup.length) {
			Object oldValue = lookup[index];
			lookup[index] = value;
			return oldValue == UNSET;
		} else {
			expandIndexedVariableTableAndSet(index, value);
			return true;
		}
	}

	public Object removeIndexedVariable(int index) {
		Object[] lookup = indexedVariables;
		if (index < lookup.length) {
			Object v = lookup[index];
			lookup[index] = UNSET;
			return v;
		} else {
			return UNSET;
		}
	}

	public boolean isIndexedVariableSet(int index) {
		Object[] lookup = indexedVariables;
		return index < lookup.length && lookup[index] != UNSET;
	}

	private void expandIndexedVariableTableAndSet(int index, Object value) {
		Object[] oldArray = indexedVariables;
		final int oldCapacity = oldArray.length;
		int newCapacity = index;
		newCapacity |= newCapacity >>> 1;
		newCapacity |= newCapacity >>> 2;
		newCapacity |= newCapacity >>> 4;
		newCapacity |= newCapacity >>> 8;
		newCapacity |= newCapacity >>> 16;
		newCapacity++;

		Object[] newArray = Arrays.copyOf(oldArray, newCapacity);
		Arrays.fill(newArray, oldCapacity, newArray.length, UNSET);
		newArray[index] = value;
		indexedVariables = newArray;
	}

	public int size() {
		int count = 0;
		if (futureListenerStackDepth != 0) {
			count ++;
		}
		if (random != null) {
			count++;
		}
		if (stringBuilder != null) {
			count++;
		}
		for (Object o : indexedVariables) {
			if (o != UNSET) {
				count++;
			}
		}
		// 我们应该从计数中减去1，因为'indexedVariables'中的第一个元素是由'FastThreadLocal'保留的，
		// 以保留'FastThreadLocal. removeall()'中要删除的'FastThreadLocal'列表。
		return count - 1;
	}

	public StringBuilder stringBuilder() {
		StringBuilder sb = stringBuilder;
		if (sb == null) {
			return stringBuilder = new StringBuilder(STRING_BUILDER_INITIAL_SIZE);
		}
		if (sb.capacity() > STRING_BUILDER_MAX_SIZE) {
			sb.setLength(STRING_BUILDER_INITIAL_SIZE);
			sb.trimToSize();
		}
		sb.setLength(0);
		return sb;
	}

	/**
	 * 获取随机数
	 */
	public ThreadLocalRandom random() {
		ThreadLocalRandom r = random;
		if (r == null) {
			random = r = new ThreadLocalRandom();
		}
		return r;
	}

	public int futureListenerStackDepth() {
		return futureListenerStackDepth;
	}

	public void setFutureListenerStackDepth(int futureListenerStackDepth) {
		this.futureListenerStackDepth = futureListenerStackDepth;
	}

}
